package com.daiyang.lottery.service.impl;

import cn.hutool.core.lang.UUID;
import com.daiyang.lottery.common.errorcode.ServiceErrorCodeConstants;
import com.daiyang.lottery.common.exception.ServiceException;
import com.daiyang.lottery.common.util.JacksonUtil;
import com.daiyang.lottery.common.util.RedisUtil;
import com.daiyang.lottery.controller.DrawPrizeController;
import com.daiyang.lottery.controller.param.DrawPrizeParam;
import com.daiyang.lottery.controller.param.ShowWinningRecordsParam;
import com.daiyang.lottery.dao.dataobject.*;
import com.daiyang.lottery.dao.mapper.*;
import com.daiyang.lottery.service.DrawPrizeService;
import com.daiyang.lottery.service.dto.WinningRecordDTO;
import com.daiyang.lottery.service.enums.ActivityPrizeStatusEnum;
import com.daiyang.lottery.service.enums.ActivityPrizeTiersEnum;
import com.daiyang.lottery.service.enums.ActivityStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collector;
import java.util.stream.Collectors;

import static com.daiyang.lottery.common.config.DirectRabbitConfig.EXCHANGE_NAME;
import static com.daiyang.lottery.common.config.DirectRabbitConfig.ROUTING;

@Service
public class DrawPrizeServiceImpl implements DrawPrizeService {
    private static final Logger logger
            = LoggerFactory.getLogger(DrawPrizeController.class);
    /**
     * 使⽤RabbitTemplate,这提供了接收/发送等等⽅法
     */
    private final String WINNING_RECORDS_PREFIX = "WINNING_RECORDS_";
    private final Long WINNING_RECORDS_TIMEOUT = 60 * 60 * 24 * 2L;
    @Autowired
    private ActivityMapper activityMapper;

    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;
    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private WinningRecordMapper winningRecordMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    RedisUtil redisUtil;
    @Override
    public void drawPrize(DrawPrizeParam param) {
        // 奖中奖信息发送给mq进行异步处理
        String messageId = String.valueOf(UUID.randomUUID());
        String messageDate= JacksonUtil.writeValueAsString(param);
        String createTime = LocalDateTime.now().
                format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));

        Map<String, Object> map= new HashMap<>();
        map.put("messageId", messageId);
        // 可以加type区分消息类型
        map.put("messageDate", messageDate);
        map.put("createTime", createTime);
        //将消息携带绑定键值：DirectRouting 发送到交换机DirectExchange
        rabbitTemplate.convertAndSend(EXCHANGE_NAME,ROUTING ,map);
        logger.info("消息发送成功：{}", JacksonUtil.writeValueAsString(map));
    }

    @Override
    public Boolean checkDrawPrizeParam(DrawPrizeParam param) {
        ActivityDO activityDO = activityMapper.selectById(param.getActivityId());

        // 奖品是否存在可以从 activity_prize, 原因是保存activity做了本地事务，保证一致性
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper
                .selectByAPId(param.getActivityId(), param.getPrizeId());
        // 活动或奖品是否存在
        if(null==activityDO||null==activityPrizeDO){
//            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY);
            logger.error("活动或奖品不存在：{}",ServiceErrorCodeConstants.ACTIVITY_OR_PRIZE_IS_EMPTY );
            return false;

        }

        // 活动是否有效
        if(activityDO.getStatus().equals(ActivityStatusEnum.COMPLETED.name())) {
         //   throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_COMPLETED);
            logger.error("活动或奖品不存在：{}",ServiceErrorCodeConstants.ACTIVITY_COMPLETED );
            return false;
        }

        // 奖品是否有效
        if(activityPrizeDO.getStatus().equalsIgnoreCase(ActivityPrizeStatusEnum.COMPLETED.name())) {
            //throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED);
            logger.error("活动或奖品不存在：{}",ServiceErrorCodeConstants.ACTIVITY_PRIZE_COMPLETED );
            return false;

        }

        // 中奖者人数是否和设置奖品数量一致 3 2
        if(activityPrizeDO.getPrizeAmount()!=param.getWinnerList().size()) {
            //throw new ServiceException(ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR);
            logger.error("活动或奖品不存在：{}",ServiceErrorCodeConstants.WINNER_PRIZE_AMOUNT_ERROR );
            return false;

        }

        return true;
     }

    @Override
    public   List<WinningRecordDO> saveWinnerRecords(DrawPrizeParam param) {
        // 插询相关的信息，活动，人员，奖品，活动关联奖品
        ActivityDO activityDO =activityMapper.selectById(param.getActivityId());
        PrizeDO prizeDO = prizeMapper.selecrtById(param.getPrizeId());
        ActivityPrizeDO activityPrizeDO = activityPrizeMapper.selectByAPId(param.getActivityId(), param.getPrizeId());
        List<UserDO> userDOList = userMapper.bathcSelectByIds(
                param.getWinnerList()
                       .stream()
                       .map(DrawPrizeParam.Winner::getUserId)
                       .collect(Collectors.toList())
        );

        // 构造中奖者记录，保存
        List<WinningRecordDO> winningRecordDOList = userDOList.stream()
                .map(userDO -> {
                    WinningRecordDO winningRecordDO = new WinningRecordDO();
                    winningRecordDO.setActivityId(activityDO.getId());
                    winningRecordDO.setActivityName(activityDO.getActivityName());
                    winningRecordDO.setPrizeId(prizeDO.getId());
                    winningRecordDO.setPrizeName(prizeDO.getName());
                    winningRecordDO.setPrizeTier(activityPrizeDO.getPrizeTiers());
                    winningRecordDO.setWinnerId(userDO.getId());
                    winningRecordDO.setWinnerName(userDO.getUserName());
                    winningRecordDO.setWinnerEmail(userDO.getEmail());
                    winningRecordDO.setWinnerPhoneNumber(userDO.getPhoneNumber());
                    winningRecordDO.setWinningTime(param.getWinningTime());
                    return winningRecordDO;
                }).collect(Collectors.toList());
        winningRecordMapper.batchInsert(winningRecordDOList);

        // 缓存中奖者记录
        // 1、缓存奖品维度中奖记录(WinningRecord_activityId_prizeId, winningRecordDOList（奖品维度的中奖名单）)
       cacheWinningRecords(param.getActivityId()+"_"+param.getPrizeId(),
               winningRecordDOList,
               WINNING_RECORDS_TIMEOUT);
        // 2、缓存活动维度中奖记录(WinningRecord_activityId, winningRecordDOList(活动维度的中奖名单))
        // 当活动已完成再去存放活动维度中奖记录
        if(activityDO.getStatus()
                .equalsIgnoreCase(ActivityStatusEnum.COMPLETED.name())) {
            List<WinningRecordDO> allList = winningRecordMapper.selectByActivityId(param.getActivityId());
            cacheWinningRecords(String.valueOf(param.getActivityId()),
                    allList,
                    WINNING_RECORDS_TIMEOUT);

        }
        return winningRecordDOList;

    }

    @Override
    public void deleteRecords(Long activtyId, Long prizeId) {
        if(null==activtyId){
            logger.warn("要删除中奖记录相关的活动id为空！");
            return;
        }
        // 删除活动维度中奖记录
        winningRecordMapper.deleteRecords(activtyId,prizeId);

        //删除缓存（奖品维度，活动维度）
        if(null!=prizeId) {
            deleteWinningRecords(activtyId+"_"+prizeId);
        }
        // 无论是否传递了prizeId，都需要删除活动维度的中奖记录缓存：
        // 如果传递了prizeId, 证明奖品未抽奖，必须删除活动维度的缓存记录
        // 如果没有传递prizeId，就只是删除活动维度的信息
        deleteWinningRecords(String.valueOf(activtyId));

    }

    @Override
    public List<WinningRecordDTO> getRecords(ShowWinningRecordsParam param) {
        String key =null == param.getPrizeId() ?
                String.valueOf(param.getActivityId()) :
                param.getActivityId()+"_"+param.getPrizeId();
        List<WinningRecordDO> winningRecordDOList=getWinningRecords(key);
        if(!CollectionUtils.isEmpty(winningRecordDOList)) {
            return convertToWinningRecordDTOList(winningRecordDOList);
        }
        winningRecordDOList = winningRecordMapper.selectByActivityIdOrPrizeId(
                param.getActivityId(), param.getPrizeId());
        // 存放记录到redis
        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            logger.info("查询的中奖记录为空！param:{}",
                    JacksonUtil.writeValueAsString(param));
            return Arrays.asList();
        }
        acheWinningRecords(key, winningRecordDOList, WINNING_RECORDS_TIMEOUT);
        return convertToWinningRecordDTOList(winningRecordDOList);
    }

    private void acheWinningRecords(String key, List<WinningRecordDO> winningRecordDOList, Long time) {
        String str = "";
        try {
            if(!StringUtils.hasText(key)
                    ||CollectionUtils.isEmpty(winningRecordDOList)) {
                logger.warn("要缓存的内容为空！key:{}, value:{}",
                        key, JacksonUtil.writeValueAsString(winningRecordDOList));
                return;
            }
            str = JacksonUtil.writeValueAsString(winningRecordDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX + key,
                    str,
                    time);
        } catch (Exception e) {
            logger.error("缓存中奖记录异常！key:{}, value:{}", WINNING_RECORDS_PREFIX + key, str);
        }
    }

    private List<WinningRecordDTO> convertToWinningRecordDTOList(List<WinningRecordDO> winningRecordDOList) {
        if(CollectionUtils.isEmpty(winningRecordDOList)) {
            return Arrays.asList();
        }
        return winningRecordDOList.stream()
                .map(winningRecordDO -> {
                    WinningRecordDTO winningRecordDTO = new WinningRecordDTO();
                    winningRecordDTO.setWinnerId(winningRecordDO.getWinnerId());
                    winningRecordDTO.setWinnerName(winningRecordDO.getWinnerName());
                    winningRecordDTO.setPrizeName(winningRecordDO.getPrizeName());
                    winningRecordDTO.setPrizeTier(
                            ActivityPrizeTiersEnum.formName(winningRecordDO.getPrizeTier()));
                    winningRecordDTO.setWinningTime(winningRecordDO.getWinningTime());
                    return winningRecordDTO;
                }).collect(Collectors.toList());
    }

    private List<WinningRecordDO> getWinningRecords(String key) {
        try{
            if(!StringUtils.hasText(key)) {
                logger.warn("要从缓存中查询中奖记录的key为空！");
                return Arrays.asList();
            }
            String str = redisUtil.get(WINNING_RECORDS_PREFIX+key);
            if(!StringUtils.hasText(str)) {
                return Arrays.asList();
            }
            return JacksonUtil.readListValue(str, WinningRecordDO.class);
        }  catch (Exception e) {
        logger.error("从缓存中查询中奖记录异常！key:{}", WINNING_RECORDS_PREFIX + key);
        return Arrays.asList();
      }
    }

    private void deleteWinningRecords(String key) {
        try{
            if(redisUtil.hasKey(WINNING_RECORDS_PREFIX+key)) {
                redisUtil.delete(WINNING_RECORDS_PREFIX+key);
            }
        }catch (Exception e){
            logger.error("删除中奖记录缓存异常，key:{}", key);
        }
    }

    private void cacheWinningRecords(String key,
                                     List<WinningRecordDO> winningRecordDOList,
                                     Long time ){
        String str="";
        try{
            if (!StringUtils.hasText(key)
                    || CollectionUtils.isEmpty(winningRecordDOList)) {
                logger.warn("要缓存的内容为空！key:{}, value:{}",
                        key, JacksonUtil.writeValueAsString(winningRecordDOList));
                return;
            }
            str =JacksonUtil.writeValueAsString(winningRecordDOList);
            redisUtil.set(WINNING_RECORDS_PREFIX+key, str, time);
        } catch (Exception e) {
            logger.error("缓存中奖记录异常！key:{}, value:{}", WINNING_RECORDS_PREFIX + key, str);
        }
    }
}
